<?php
/**
 * Contains the settings class.
 * @package waterwheel
 *
 */
/*
 *  Copyright 2010 Andrew Rickmann
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */




/**
 * The settings class provides an interface to access the applications settings.
 * The applications settings are namespaced for control and cleanup purposes.
 *
 * Usage requires only that a new object is instantiated.
 *
 * <code>
 * $mysettings = new settings();
 * </code>
 *
 *
 * @author Andrew Rickmann
 * @version 0.1
 * 
 */
class settings {

    /**
     * Holds a static copy of everything that has been retrieved from the database
     * to reduce unnecessary database acccess. The class methods update this when
     * settings and namespaces are added, modified, or removed.
     *
     * @var Array $namespaces 
     */
    private static $namespaces = null;

    /**
     *
     * @var String the namespace to use by default for all calls to this instance
     */
    private $instance_namespace;

    /**
     * Sets a default namespace for this instance to use as the default
     * This allows objects, such as plugins, to hold an instance with a pre-set namespace
     *
     * @param String $namespace the namespace
     */
    function use_namespace( $namespace ){
	$this->instance_namespace = $namespace;
    }

    /**
     *	Retrieves all of the settings belonging to a particular namespace and returns them as an array.
     *
     * If the settings already exists in the static namespaces cache then this will be returned instead.
     * If it does not exist in the static cache the settings will be retrieved from the database and
     * the cache will be populated.
     *
     * Most of the other methods that retrieve settings in this class will call this function first
     * and then use the results, this makes sure the cache is populated early with all the settings
     * needed by each namespace so repeated database calls are not needed.
     *
     * @param String $namespace The namespace name
     * @return Array An array containing all the key => value pairs in the namespace
     */
    function get_namespace( $namespace ){

	//see if it is in the cache
	if ( isset( self::$namespaces[$namespace] ) ){
	    return self::$namespaces[$namespace];
	}
	
	//if not get from database
	//create blank array
	$settings = array();

	//get results from the database
	$namespacequery = 'SELECT * FROM settings WHERE setting_namespace = ?';
	$namespaceparam = array($namespace);
	$results = databaseconnection::query($namespacequery,$namespaceparam);

	//make sure there are some results
	if ( count($results) == 0 ){ return false;}
	
	//load the results into the array
	foreach( $results as $result ){
	    $settings[$result['setting_key']] = $result['setting_value'];
	}
	
	//add to the cache
	self::$namespaces[$namespace] = $settings;

	//return the values
	return $settings;
    }


    /**
     *	Retrieve a single setting attached to a specific namespace
     *
     * At the current time this DOES NOT unserialize data.
     *
     *
     * @param String $key The name of the setting
     * @param String $namespace The namespace the setting is contained in. Will default to 'global' if not specified.
     * @return String | bool Returns the value of the setting or false if the setting does not exist.
     */
    function get( $key , $namespace = null ){

	//deal with null namespaces
	if ( is_null($namespace) ){
	    $namespace = ( isset($this->instance_namespace) ) ? $this->instance_namespace : 'global';
	}

	//get the namespace
	$namespace = $this->get_namespace($namespace);
	//check if the settings exists
	if ( isset( $namespace[$key] ) ){
	    return $namespace[$key];
	} else {
	    return false;
	}

    }


    /**
     * Creates a new setting, or updates an existing setting.
     *
     * It is recommended that a namespace is always specified so that the database
     * can be cleaned up easily if those settings are no longer needed.
     *
     * Note that this function does NOT serialize arrays.
     *
     *
     * @param String $key The name of the setting
     * @param String $value The value of the setting
     * @param String $namespace The namespace to place the setting in. Defaults to 'global' if not set.
     * @return String The value of the setting
     */
    function set( $key , $value , $namespace = null ){

	//deal with null namespaces
	if ( is_null($namespace) ){
	    $namespace = ( isset($this->instance_namespace) ) ? $this->instance_namespace : 'global';
	}

	//figure out if this namespace / key pair already exists
	$updatequery = databaseconnection::queryScalar('SELECT COUNT(setting_id) as count FROM settings WHERE setting_namespace = ? AND setting_key = ?' , array( $namespace, $key ));

	if ( $updatequery == 1 ){
	    //if it exists, update
	    $updatequery = 'UPDATE settings SET setting_value = ? WHERE setting_namespace = ? AND setting_key = ?';
	    $updateparams = array( $value, $namespace , $key );
	    databaseconnection::query($updatequery, $updateparams);
	} else {
	    $insertquery = 'INSERT INTO settings (setting_value,setting_namespace,setting_key) Values (?,?,?)';
	    $insertparams = array( $value, $namespace , $key );
	    databaseconnection::query($insertquery, $insertparams);
	}

	//update the internal
	self::$namespaces[$namespace][$key] = $value;

	return $value;

    }

    /**
     *	Delete a setting
     *
     * @todo Produce a list of protected settings to prevent bad removals
     * @todo Make it return false if it fails because of protection
     *
     *
     * @param String $key The name of the setting
     * @param String $namespace The namespace the setting is in
     * @return Bool Returns true
     */
    function remove( $key , $namespace = null ){

	//deal with null namespaces
	if ( is_null($namespace) ){
	    $namespace = ( isset($this->instance_namespace) ) ? $this->instance_namespace : 'global';
	}

	//delete from database
	$deletequery = 'DELETE FROM settings WHERE setting_key = ? and setting_namespace = ?';
	$deleteparams = array( $key , $namespace );
	databaseconnection::query( $deletequery , $deleteparams );

	//delete from static cache
	unset( self::$namespaces[$namespace][$key] );

	return true;

    }

    /**
     * An Alias of the remove_namespace function which makes use of the
     * instance namespace setting.
     * 
     * @param String $namespace
     */
    function remove_settings( $namespace = null ){

	if ( is_null($namespace) ){
	   if ( isset($this->instance_namespace) ) {
	       $namespace = $this->instance_namespace;
	   } else {
	       return;
	   }
	}

	return $this->remove_namespace($namespace);

    }




    /**
     *	Delete all the settings in a particular namespace
     *
     * Will not take any action against the 'system', 'global', or 'defaults' namespaces.
     *
     * @todo move the protected settings to a static variable and include functions to indicate they are protected.
     *
     *
     * @param String $namespace
     * @return bool Returns false if the namespace is protected
     */
    function remove_namespace( $namespace ){

	//cannot remove protected namespaces
	$protected = array('system','global','defaults');
	if ( in_array( $namespace , $protected ) ){
	    return false;
	}

	//otherwise delete all settings in the namespace
	$settings = $this->get_namespace($namespace);

	if ( !$settings ){
	    return false;
	}

	foreach( $settings as $key => $value ){
	    $this->remove( $key , $namespace);
	}

	//delete from static cache
	unset( self::$namespaces[$namespace] );

	return true;

    }


    function __get( $namespace ){
	return $this->get_namespace($namespace);

    }

}
?>
